"""
通知服务
处理通知的创建、查询、更新等操作
"""
import logging
from typing import List, Optional, Dict, Any
from datetime import datetime, timezone
from motor.motor_asyncio import AsyncIOMotorDatabase
from bson import ObjectId
from pymongo import DESCENDING, ASCENDING

from app.models.notification import (
    Notification,
    NotificationInDB,
    NotificationResponse,
    NotificationCreate,
    NotificationUpdate,
    NotificationType,
    RelatedObjectType
)
from app.models.notification import NotificationSettings
from app.utils.email_sender import EmailSender

logger = logging.getLogger(__name__)


class NotificationService:
    """通知服务类"""
    
    @staticmethod
    async def create_notification(
        db: AsyncIOMotorDatabase,
        notification_data: NotificationCreate,
        send_email: bool = False
    ) -> Optional[NotificationResponse]:
        """
        创建通知
        
        Args:
            db: 数据库连接
            notification_data: 通知数据
            send_email: 是否发送邮件通知
        
        Returns:
            NotificationResponse: 创建的通知
        """
        try:
            # 验证用户ID
            user_id = ObjectId(notification_data.user_id)
            user = await db.users.find_one({"_id": user_id})
            if not user:
                logger.warning(f"用户不存在: {notification_data.user_id}")
                return None
            
            # 获取用户通知设置
            settings_doc = await db.notification_settings.find_one({"user_id": user_id})
            if not settings_doc:
                # 使用默认设置
                settings = NotificationSettings()
            else:
                settings = NotificationSettings(**settings_doc)
            
            # 检查用户是否启用了该类型的系统通知
            notification_type = notification_data.type.value
            system_notification_enabled = False
            
            if notification_type == NotificationType.TASK_ASSIGNED.value:
                system_notification_enabled = settings.system_task_assigned
            elif notification_type == NotificationType.TASK_COMPLETED.value:
                system_notification_enabled = settings.system_task_completed
            elif notification_type == NotificationType.PROJECT_UPDATED.value:
                system_notification_enabled = settings.system_project_update
            elif notification_type == NotificationType.TASK_DEADLINE_REMINDER.value:
                system_notification_enabled = settings.system_deadline_reminder
            else:
                # 其他类型默认启用
                system_notification_enabled = True
            
            # 如果用户未启用系统通知，则不创建通知记录
            if not system_notification_enabled:
                logger.info(f"用户 {notification_data.user_id} 未启用 {notification_type} 类型的系统通知")
                # 但仍然可以发送邮件（如果启用）
                send_email = send_email and (
                    (notification_type == NotificationType.TASK_ASSIGNED.value and settings.email_task_assigned) or
                    (notification_type == NotificationType.TASK_COMPLETED.value and settings.email_task_completed) or
                    (notification_type == NotificationType.PROJECT_UPDATED.value and settings.email_project_update) or
                    (notification_type == NotificationType.TASK_DEADLINE_REMINDER.value and settings.email_deadline_reminder)
                )
            else:
                # 创建通知记录
                notification_doc = {
                    "user_id": user_id,
                    "type": notification_data.type.value,
                    "title": notification_data.title,
                    "content": notification_data.content,
                    "related_type": notification_data.related_type.value if notification_data.related_type else None,
                    "related_id": ObjectId(notification_data.related_id) if notification_data.related_id else None,
                    "is_read": False,
                    "read_at": None,
                    "created_at": datetime.now(timezone.utc),
                    "metadata": notification_data.metadata or {}
                }
                
                result = await db.notifications.insert_one(notification_doc)
                notification_doc["_id"] = result.inserted_id
                
                # 转换为响应模型
                notification = NotificationService._doc_to_response(notification_doc)
            
            # 发送邮件通知（如果需要）
            if send_email and user.get("email"):
                try:
                    # 构建相关URL（如果有）
                    related_url = None
                    if notification_data.related_id and notification_data.related_type:
                        base_url = "http://localhost:3000"  # 可以从配置中获取
                        if notification_data.related_type == RelatedObjectType.TASK:
                            related_url = f"{base_url}/tasks/{notification_data.related_id}"
                        elif notification_data.related_type == RelatedObjectType.PROJECT:
                            related_url = f"{base_url}/projects/{notification_data.related_id}"
                    
                    await EmailSender.send_notification_email(
                        db=db,
                        to_email=user["email"],
                        notification_type=notification_type,
                        title=notification_data.title,
                        content=notification_data.content,
                        related_url=related_url
                    )
                except Exception as e:
                    logger.error(f"发送邮件通知失败: {str(e)}")
            
            if system_notification_enabled:
                return notification
            return None
            
        except Exception as e:
            logger.error(f"创建通知失败: {str(e)}")
            return None
    
    @staticmethod
    async def create_batch_notifications(
        db: AsyncIOMotorDatabase,
        notifications_data: List[NotificationCreate],
        send_email: bool = False
    ) -> List[NotificationResponse]:
        """
        批量创建通知
        
        Args:
            db: 数据库连接
            notifications_data: 通知数据列表
            send_email: 是否发送邮件通知
        
        Returns:
            List[NotificationResponse]: 创建的通知列表
        """
        results = []
        for notification_data in notifications_data:
            notification = await NotificationService.create_notification(
                db=db,
                notification_data=notification_data,
                send_email=send_email
            )
            if notification:
                results.append(notification)
        return results
    
    @staticmethod
    async def get_user_notifications(
        db: AsyncIOMotorDatabase,
        user_id: str,
        page: int = 1,
        limit: int = 20,
        is_read: Optional[bool] = None,
        notification_type: Optional[str] = None,
        sort_by: str = "created_at",
        sort_order: str = "desc",
        is_admin: bool = False
    ) -> Dict[str, Any]:
        """
        获取用户通知列表
        
        Args:
            db: 数据库连接
            user_id: 用户ID（如果是管理员，可以为空查看所有）
            page: 页码
            limit: 每页数量
            is_read: 是否已读（可选）
            notification_type: 通知类型（可选）
            sort_by: 排序字段
            sort_order: 排序方向
            is_admin: 是否为管理员（管理员可以查看所有通知）
        
        Returns:
            Dict: 包含通知列表和总数
        """
        try:
            # 构建查询条件
            query = {}
            if not is_admin:
                # 非管理员只能查看自己的通知
                user_obj_id = ObjectId(user_id)
                query["user_id"] = user_obj_id
            # 管理员不添加 user_id 过滤条件，可以查看所有通知
            
            if is_read is not None:
                query["is_read"] = is_read
            if notification_type:
                query["type"] = notification_type
            
            # 排序
            sort_direction = DESCENDING if sort_order == "desc" else ASCENDING
            sort_field = sort_by if sort_by in ["created_at", "is_read"] else "created_at"
            
            # 计算跳过的记录数
            skip = (page - 1) * limit
            
            # 查询总数
            total = await db.notifications.count_documents(query)
            
            # 查询通知列表
            cursor = db.notifications.find(query).sort(sort_field, sort_direction).skip(skip).limit(limit)
            notifications_docs = await cursor.to_list(length=limit)
            
            # 转换为响应模型并序列化 datetime 字段
            notifications = []
            for doc in notifications_docs:
                notification = NotificationService._doc_to_response(doc)
                # 将 Pydantic 模型转换为字典，确保 datetime 字段被序列化
                notification_dict = notification.model_dump(mode='json')
                notifications.append(notification_dict)
            
            return {
                "data": notifications,
                "total": total,
                "page": page,
                "limit": limit
            }
            
        except Exception as e:
            logger.error(f"获取用户通知列表失败: {str(e)}")
            return {
                "data": [],
                "total": 0,
                "page": page,
                "limit": limit
            }
    
    @staticmethod
    async def get_unread_count(
        db: AsyncIOMotorDatabase,
        user_id: str,
        is_admin: bool = False
    ) -> int:
        """
        获取用户未读通知数量
        
        Args:
            db: 数据库连接
            user_id: 用户ID（如果是管理员，可以为空查看所有）
            is_admin: 是否为管理员（管理员可以查看所有未读通知）
        
        Returns:
            int: 未读通知数量
        """
        try:
            query = {"is_read": False}
            if not is_admin:
                # 非管理员只能查看自己的未读通知
                user_obj_id = ObjectId(user_id)
                query["user_id"] = user_obj_id
            # 管理员不添加 user_id 过滤条件，可以查看所有未读通知
            
            count = await db.notifications.count_documents(query)
            return count
        except Exception as e:
            logger.error(f"获取未读通知数量失败: {str(e)}")
            return 0
    
    @staticmethod
    async def mark_as_read(
        db: AsyncIOMotorDatabase,
        notification_id: str,
        user_id: str,
        is_admin: bool = False
    ) -> bool:
        """
        标记通知为已读
        
        Args:
            db: 数据库连接
            notification_id: 通知ID
            user_id: 用户ID（用于验证权限）
            is_admin: 是否为管理员（管理员可以标记任何通知为已读）
        
        Returns:
            bool: 是否成功
        """
        try:
            notification_obj_id = ObjectId(notification_id)
            user_obj_id = ObjectId(user_id)
            
            # 构建查询条件
            query = {"_id": notification_obj_id}
            if not is_admin:
                # 非管理员只能标记自己的通知为已读
                query["user_id"] = user_obj_id
            
            # 验证通知是否存在（管理员可以标记任何通知，非管理员只能标记自己的）
            notification = await db.notifications.find_one(query)
            
            if not notification:
                return False
            
            # 更新为已读
            await db.notifications.update_one(
                {"_id": notification_obj_id},
                {
                    "$set": {
                        "is_read": True,
                        "read_at": datetime.now(timezone.utc)
                    }
                }
            )
            
            return True
            
        except Exception as e:
            logger.error(f"标记通知为已读失败: {str(e)}")
            return False
    
    @staticmethod
    async def mark_all_as_read(
        db: AsyncIOMotorDatabase,
        user_id: str,
        is_admin: bool = False
    ) -> int:
        """
        标记所有通知为已读
        
        Args:
            db: 数据库连接
            user_id: 用户ID（如果是管理员，可以为空标记所有通知）
            is_admin: 是否为管理员（管理员可以标记所有通知为已读）
        
        Returns:
            int: 更新的通知数量
        """
        try:
            query = {"is_read": False}
            if not is_admin:
                # 非管理员只能标记自己的通知为已读
                user_obj_id = ObjectId(user_id)
                query["user_id"] = user_obj_id
            # 管理员不添加 user_id 过滤条件，可以标记所有通知为已读
            
            result = await db.notifications.update_many(
                query,
                {
                    "$set": {
                        "is_read": True,
                        "read_at": datetime.now(timezone.utc)
                    }
                }
            )
            
            return result.modified_count
            
        except Exception as e:
            logger.error(f"标记所有通知为已读失败: {str(e)}")
            return 0
    
    @staticmethod
    async def delete_notification(
        db: AsyncIOMotorDatabase,
        notification_id: str,
        user_id: str,
        is_admin: bool = False
    ) -> bool:
        """
        删除通知
        
        Args:
            db: 数据库连接
            notification_id: 通知ID
            user_id: 用户ID（用于验证权限，管理员可忽略）
            is_admin: 是否为管理员（管理员可以删除任何通知）
        
        Returns:
            bool: 是否成功
        """
        try:
            notification_obj_id = ObjectId(notification_id)
            
            # 构建查询条件
            query = {"_id": notification_obj_id}
            if not is_admin:
                # 非管理员只能删除自己的通知
                user_obj_id = ObjectId(user_id)
                query["user_id"] = user_obj_id
            # 管理员不添加 user_id 过滤条件，可以删除任何通知
            
            # 删除通知
            result = await db.notifications.delete_one(query)
            
            return result.deleted_count > 0
            
        except Exception as e:
            logger.error(f"删除通知失败: {str(e)}")
            return False
    
    @staticmethod
    async def delete_batch_notifications(
        db: AsyncIOMotorDatabase,
        notification_ids: List[str],
        user_id: str,
        is_admin: bool = False
    ) -> int:
        """
        批量删除通知
        
        Args:
            db: 数据库连接
            notification_ids: 通知ID列表
            user_id: 用户ID（用于验证权限，管理员可忽略）
            is_admin: 是否为管理员（管理员可以删除任何通知）
        
        Returns:
            int: 删除的通知数量
        """
        try:
            notification_obj_ids = [ObjectId(nid) for nid in notification_ids]
            
            # 构建查询条件
            query = {"_id": {"$in": notification_obj_ids}}
            if not is_admin:
                # 非管理员只能删除自己的通知
                user_obj_id = ObjectId(user_id)
                query["user_id"] = user_obj_id
            # 管理员不添加 user_id 过滤条件，可以删除任何通知
            
            result = await db.notifications.delete_many(query)
            
            return result.deleted_count
            
        except Exception as e:
            logger.error(f"批量删除通知失败: {str(e)}")
            return 0
    
    @staticmethod
    def _doc_to_response(doc: dict) -> NotificationResponse:
        """将数据库文档转换为响应模型"""
        return NotificationResponse(
            id=str(doc["_id"]),
            user_id=str(doc["user_id"]),
            type=doc["type"],
            title=doc["title"],
            content=doc["content"],
            related_type=doc.get("related_type"),
            related_id=str(doc["related_id"]) if doc.get("related_id") else None,
            is_read=doc.get("is_read", False),
            read_at=doc.get("read_at"),
            created_at=doc.get("created_at", datetime.now(timezone.utc)),
            metadata=doc.get("metadata", {})
        )

